/*
 * socklib.c
 *
 * 该文件包含在编写互联网客户端/服务器程序时常用的函数。
 * 这里的两个主要函数是：
 *
 * make_server_socket(portnum) 返回一个服务器套接字
 * 或者在出错时返回 -1
 * make_server_socket_q(portnum, backlog)
 *
 * connect_to_server(char *hostname, int portnum)
 * 返回一个已连接的套接字
 * 或者在出错时返回 -1
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>

#define HOSTLEN 256
#define BACKLOG 1

int make_server_socket_q(int portnum, int backlog);

// 创建服务器套接字，使用默认的 BACKLOG 值
int make_server_socket(int portnum) {
    return make_server_socket_q(portnum, BACKLOG);
}

// 创建服务器套接字并设置监听队列长度
int make_server_socket_q(int portnum, int backlog) {
    struct sockaddr_in saddr;   // 用于构建地址
    struct hostent *hp;         // 主机信息
    char hostname[HOSTLEN];     // 主机名
    int sock_id;                // 套接字标识符

    // 创建套接字
    sock_id = socket(PF_INET, SOCK_STREAM, 0);
    if (sock_id == -1) {
        perror("创建套接字失败");
        return -1;
    }

    // 构建地址并绑定到套接字
    memset(&saddr, 0, sizeof(saddr)); // 清空结构体
    if (gethostname(hostname, HOSTLEN) == -1) {
        perror("获取主机名失败");
        close(sock_id);
        return -1;
    }
    hp = gethostbyname(hostname); // 获取主机信息
    if (hp == NULL) {
        perror("获取主机信息失败");
        close(sock_id);
        return -1;
    }
    memcpy(&saddr.sin_addr, hp->h_addr, hp->h_length); // 填充主机地址
    saddr.sin_port = htons(portnum); // 填充套接字端口
    saddr.sin_family = AF_INET;      // 填充地址族

    // 绑定套接字
    if (bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0) {
        perror("绑定失败");
        close(sock_id);
        return -1;
    }

    // 设置监听
    if (listen(sock_id, backlog) != 0) {
        perror("监听失败");
        close(sock_id);
        return -1;
    }
    return sock_id;
}

// 连接到服务器
int connect_to_server(char *host, int portnum) {
    int sock;
    struct sockaddr_in servadd; // 服务器地址
    struct hostent *hp;         // 主机信息

    // 第一步：获取套接字
    sock = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字
    if (sock == -1) {
        perror("创建套接字失败");
        return -1;
    }

    // 第二步：连接到服务器
    memset(&servadd, 0, sizeof(servadd)); // 清空地址结构
    hp = gethostbyname(host); // 查找主机的 IP 地址
    if (hp == NULL) {
        perror("获取主机信息失败");
        close(sock);
        return -1;
    }
    memcpy(&servadd.sin_addr, hp->h_addr, hp->h_length); // 填充服务器地址
    servadd.sin_port = htons(portnum); // 填充端口号
    servadd.sin_family = AF_INET;      // 填充地址族

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&servadd, sizeof(servadd)) != 0) {
        perror("连接失败");
        close(sock);
        return -1;
    }

    return sock; // 返回已连接的套接字
}

